# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
# This source file is part of the Cangjie project, licensed under Apache-2.0
# with Runtime Library Exception.
#
# See https://cangjie-lang.cn/pages/LICENSE for license information.


from os import path
import random

integer_types = ['Int8', 'Int16', 'Int32', 'Int64', 'IntNative', 'UInt8', 'UInt16', 'UInt32', 'UInt64', 'UIntNative']
number_types = integer_types + ['Float16', 'Float32', 'Float64']
other_types = ['String', 'Rune', 'Bool', 'Unit', '(Int8, Int8)', 'Array<Int8>']
types = number_types + other_types
default_value_map = {'Bool' : 'true', 'Unit' : '()', 'Array<Int8>' : '[1]', 'Rune' : "'1'", 'String' : '"1"', '(Int8, Int8)' : '(1i8, 1i8)'}
ops = ['', '+', '-', '*', '/', '**', '%', '&&', '||', '&', '|', '^', '<<', '>>']
random.seed(123)

def default_value(ty : str) -> str:
  if ty in integer_types: return '1'
  if 'Float' in ty: return '1.0'
  return default_value_map[ty]

def valid_types(op: str) -> list:
  if op in ['%', '<<', '>>', '&', '|', '^']: return integer_types
  if op in ['&&', '||']: return ['Bool']
  if op == '**': return ['Float64']
  if not op: return types
  return number_types

dir = path.dirname(path.realpath(__file__))
path = dir + '/test_' + path.basename(dir) + '_{}.cj'
template = '''
/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
 * This source file is part of the Cangjie project, licensed under Apache-2.0
 * with Runtime Library Exception.
 *
 * See https://cangjie-lang.cn/pages/LICENSE for license information.
 */

/*
  @Assertion:   4.23(15) A left value expression is legal only if it is mutable. For the mutability of the above
                expressions, see the corresponding sections.
  @Description: %s
  @Mode: compileonly
  @Negative: yes
  @Structure: single
  @CompileWarning: ignore
  @Comment: Auto-generated by gen.py
*/
%s
main() {{
%s
    return 0
}}
'''
templates = []
templates.append(template % ('Checks that variables assigned with let are cannot be assigned by {op}=.', '', '''    let v = {v}
    v {op}= {v}'''))
templates.append(template % ('Checks that tuple element cannot be assigned by {op}=.', '', '''    var t = ({v}, 1)
    t[0] {op}= {v}'''))
templates.append(template % ('''Checks that e.a cannot be assigned with {op}= when a is an immutable instance member variable
                and type of e is a reference type.''',
'''
class C {{
    let x = {v}
}}

class D {{
    func getC() {{ c }}

    var c = C()
}}
''', '''    var d = D()
    d.getC().x {op}= {v}'''))
templates.append(template % ('Check that e.a cannot be assigned with {op}= for value type of e when e is immutable.',
'''
struct S {{
    S(var x!: {t}) {{}}
}}
''', '''    let s = S(x: {v})
    s.x {op}= {v}'''))
templates.append(template % ('''Check that e.a cannot be assigned with {op}= for value type of e when e is mutable
                and a is an immutable instance member variable''',
'''
struct S {{
    S(let x!: {t}) {{}}
}}
''', '''    var s = S(x: {v})
    s.x {op}= {v}'''))
templates.append(template % ('''Check that 'a[b]' cannot be assigned with {op}= for a of type T when T does not overload
                the set form of the operator [].''',
'''
class C {{
    var x = {v}

    operator func [](y : Int64) {{ x }}
}}
''', '''    var c = C()
    c[0] {op}= {v}'''))
templates.append(template % ('''Check that immutable optional chaining expression being used on left-hand side of assignment expression
                causes a compilation error. Check case 'a?.b = e1'.''',
'''
class C {{
    let value = {v}
}}
''', '''    var c: ?C = C()
    c?.value {op}= {v}'''))
templates.append(template % ('''Check that immutable optional chaining expression being used on left-hand side of assignment
                expression ({op}=) causes a compilation error. Check case 'a?[b] = e2'.''',
'''
class C {{
    let value = {v}

    %s
}}
''' % '\n\n    '.join(['operator func %s(other : C) {{ C() }}' % op for op in ops[1:]]),
'''    var t: (C, C, C) = (C(), C(), C())
    t?[1] {op}= C()'''))
templates.append(template % ('''Check that immutable optional chaining expression being used on left-hand side of assignment
                expression ({op}=) causes a compilation error. Check case 'a?.b.c?.d = e1'.''',
'''
struct C {{
    let d = {v}
}}

struct B {{
    var c = Option<C>.Some(C())
}}

struct A {{
    var b = B()
}}
''', '''    var a = Option<A>.Some(A())
    a?.b.c?.d {op}= {v}'''))
templates.append(template % ('''Check that 'a?.b' cannot be assigned with {op}= when T (type of 'a') is a reference type, and 'b' is
                immutable.''',
'''
class A {{
    let b = {v}
}}

''', '''    var a: ?A = A()
    a?.b {op}= {v}'''))
templates.append(template % ('''Check that 'a?.b' cannot be assigned with {op}= when T (type of 'a') is not a reference type (struct).''',
'''
struct A {{
    var b = {v}
}}

''', '''    var a: ?A = A()
    a?.b {op}= {v}'''))
templates.append(template % ('''Check that 'a?[b]' cannot be assigned with {op}= if reference type T does not overload the set form
                of the operator [].''',
'''
class C {{
    var x = {v}

    operator func [](y : Int64) {{ x }}
}}

''', '''    var c: ?C = C()
    c?[0] {op}= {v}'''))
templates.append(template % ('''Check that 'a?[b]' cannot be assigned with {op}= when T is not a reference type (struct).''',
'''
struct S {{
    operator func[](i: Int64, value!: {t}) {{}}
}}

''', '''    var s: ?S = S(x: 3)
    s?[0] {op}= {v}'''))
templates.append(template % ('''Check that function parameter cannot be assigned with {op}=.''',
'''
func f(x : {t}) {{
    x {op}= {v}
}}

''', '''    f({v})'''))
templates.append(template % ('''Check that immutable static variable cannot be assigned with {op}=.''',
'''
class C {{
    static let x = {v}
}}

''', '''    C.x {op}= v'''))

counter = 6

def write_counted(contents : str):
  global counter
  with open(path.format(str(counter).zfill(3)), 'w') as file:
    file.write(contents)
    counter += 1

for template in templates:
  for test in [template.format(t=t, v=default_value(t), op=op) for op in ops for t in [random.choice(valid_types(op))]]:
    write_counted(test)
